package com.ethan.limit.aop;

import com.ethan.limit.annotions.RateLimiter;
import com.ethan.limit.enums.LimitType;
import com.ethan.limit.until.IpUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * Description of the class.
 *
 * @author ethan
 * @version 1.0.0
 * @since 2024-03-26
 */
@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class LimiterAspect {

    private final RedisTemplate<Object, Object> redisTemplate;

    @Before("@annotation(rateLimiter)")
    public void before(JoinPoint point, RateLimiter rateLimiter) throws Exception {
        int time = rateLimiter.time();
        int count = rateLimiter.count();
        String key = getKey(point, rateLimiter);
        try {
            Long increment = redisTemplate.opsForValue().increment(key, 1);
            if (increment!=null&&increment==1) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            if (redisTemplate.getExpire(key)==-1){
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            if (increment>count){
                log.error("访问太频繁请稍后再试");
                throw new Exception("访问太频繁请稍后再试" );
            }

        } catch (Exception e) {
            log.error("异常：" + e.getMessage());
            throw new Exception("异常：" + e.getMessage());
        }
    }

    private String getKey(JoinPoint point, RateLimiter rateLimiter) {
        StringBuilder stringBuilder = new StringBuilder(rateLimiter.key());
        if (rateLimiter.limitType() == (LimitType.IP)) {
            stringBuilder.append(IpUtil.getIpAddr(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()));
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        stringBuilder.append(method.getDeclaringClass()).append("-").append(method.getName());
        return stringBuilder.toString();
    }

}
